/*
 * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.dynamicany;

import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.Any;
import org.omg.CORBA.TypeCodePackage.BadKind;
import org.omg.CORBA.TypeCodePackage.Bounds;
import org.omg.CORBA.portable.InputStream;
import org.omg.DynamicAny.*;
import org.omg.DynamicAny.DynAnyPackage.TypeMismatch;
import org.omg.DynamicAny.DynAnyPackage.InvalidValue;
import org.omg.DynamicAny.DynAnyFactoryPackage.InconsistentTypeCode;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;

public class DynUnionImpl extends DynAnyConstructedImpl implements DynUnion {
  //
  // Instance variables
  //

  DynAny discriminator = null;
  // index either points to the discriminator or the named member is it exists.
  // The currently active member, which is of the same type as the discriminator.
  DynAny currentMember = null;
  int currentMemberIndex = NO_INDEX;

  //
  // Constructors
  //

  private DynUnionImpl() {
    this(null, (Any) null, false);
  }

  protected DynUnionImpl(ORB orb, Any any, boolean copyValue) {
    // We can be sure that typeCode is of kind tk_union
    super(orb, any, copyValue);
  }

  protected DynUnionImpl(ORB orb, TypeCode typeCode) {
    // We can be sure that typeCode is of kind tk_union
    super(orb, typeCode);
  }

  protected boolean initializeComponentsFromAny() {
    try {
      InputStream input = any.create_input_stream();
      Any discriminatorAny = DynAnyUtil.extractAnyFromStream(discriminatorType(), input, orb);
      discriminator = DynAnyUtil.createMostDerivedDynAny(discriminatorAny, orb, false);
      currentMemberIndex = currentUnionMemberIndex(discriminatorAny);
      Any memberAny = DynAnyUtil.extractAnyFromStream(memberType(currentMemberIndex), input, orb);
      currentMember = DynAnyUtil.createMostDerivedDynAny(memberAny, orb, false);
      components = new DynAny[]{discriminator, currentMember};
    } catch (InconsistentTypeCode ictc) { // impossible
    }
    return true;
  }

  // Sets the current position to zero.
  // The discriminator value is set to a value consistent with the first named member
  // of the union. That member is activated and (recursively) initialized to its default value.
  protected boolean initializeComponentsFromTypeCode() {
    //System.out.println(this + " initializeComponentsFromTypeCode");
    try {
      // We can be sure that memberCount() > 0 according to the IDL language spec
      discriminator = DynAnyUtil.createMostDerivedDynAny(memberLabel(0), orb, false);
      index = 0;
      currentMemberIndex = 0;
      currentMember = DynAnyUtil.createMostDerivedDynAny(memberType(0), orb);
      components = new DynAny[]{discriminator, currentMember};
    } catch (InconsistentTypeCode ictc) { // impossible
    }
    return true;
  }

  //
  // Convenience methods
  //

  private TypeCode discriminatorType() {
    TypeCode discriminatorType = null;
    try {
      discriminatorType = any.type().discriminator_type();
    } catch (BadKind bad) {
    }
    return discriminatorType;
  }

  private int memberCount() {
    int memberCount = 0;
    try {
      memberCount = any.type().member_count();
    } catch (BadKind bad) {
    }
    return memberCount;
  }

  private Any memberLabel(int i) {
    Any memberLabel = null;
    try {
      memberLabel = any.type().member_label(i);
    } catch (BadKind bad) {
    } catch (Bounds bounds) {
    }
    return memberLabel;
  }

  private TypeCode memberType(int i) {
    TypeCode memberType = null;
    try {
      memberType = any.type().member_type(i);
    } catch (BadKind bad) {
    } catch (Bounds bounds) {
    }
    return memberType;
  }

  private String memberName(int i) {
    String memberName = null;
    try {
      memberName = any.type().member_name(i);
    } catch (BadKind bad) {
    } catch (Bounds bounds) {
    }
    return memberName;
  }

  private int defaultIndex() {
    int defaultIndex = -1;
    try {
      defaultIndex = any.type().default_index();
    } catch (BadKind bad) {
    }
    return defaultIndex;
  }

  private int currentUnionMemberIndex(Any discriminatorValue) {
    int memberCount = memberCount();
    Any memberLabel;
    for (int i = 0; i < memberCount; i++) {
      memberLabel = memberLabel(i);
      if (memberLabel.equal(discriminatorValue)) {
        return i;
      }
    }
    if (defaultIndex() != -1) {
      return defaultIndex();
    }
    return NO_INDEX;
  }

  protected void clearData() {
    super.clearData();
    discriminator = null;
    // Necessary to guarantee OBJECT_NOT_EXIST in member()
    currentMember.destroy();
    currentMember = null;
    currentMemberIndex = NO_INDEX;
  }

  //
  // DynAny interface methods
  //

  // _REVISIT_ More efficient copy operation

  //
  // DynUnion interface methods
  //

  /**
   * Returns the current discriminator value.
   */
  public org.omg.DynamicAny.DynAny get_discriminator() {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    return (checkInitComponents() ? discriminator : null);
  }

  // Sets the discriminator of the DynUnion to the specified value.
  // If the TypeCode of the parameter is not equivalent
  // to the TypeCode of the unions discriminator, the operation raises TypeMismatch.
  //
  // Setting the discriminator to a value that is consistent with the currently
  // active union member does not affect the currently active member.
  // Setting the discriminator to a value that is inconsistent with the currently
  // active member deactivates the member and activates the member that is consistent
  // with the new discriminator value (if there is a member for that value)
  // by initializing the member to its default value.
  //
  // If the discriminator value indicates a non-existent union member
  // this operation sets the current position to 0
  // (has_no_active_member returns true in this case).
  // Otherwise the current position is set to 1 (has_no_active_member returns false and
  // component_count returns 2 in this case).
  public void set_discriminator(org.omg.DynamicAny.DynAny newDiscriminator)
      throws org.omg.DynamicAny.DynAnyPackage.TypeMismatch {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    if (!newDiscriminator.type().equal(discriminatorType())) {
      throw new TypeMismatch();
    }
    newDiscriminator = DynAnyUtil.convertToNative(newDiscriminator, orb);
    Any newDiscriminatorAny = getAny(newDiscriminator);
    int newCurrentMemberIndex = currentUnionMemberIndex(newDiscriminatorAny);
    if (newCurrentMemberIndex == NO_INDEX) {
      clearData();
      index = 0;
    } else {
      // _REVISIT_ Could possibly optimize here if we don't need to initialize components
      checkInitComponents();
      if (currentMemberIndex == NO_INDEX || newCurrentMemberIndex != currentMemberIndex) {
        clearData();
        index = 1;
        currentMemberIndex = newCurrentMemberIndex;
        try {
          currentMember = DynAnyUtil.createMostDerivedDynAny(memberType(currentMemberIndex), orb);
        } catch (InconsistentTypeCode ictc) {
        }
        discriminator = newDiscriminator;
        components = new DynAny[]{discriminator, currentMember};
        representations = REPRESENTATION_COMPONENTS;
      }
    }
  }

  // Sets the discriminator to a value that is consistent with the value
  // of the default case of a union; it sets the current position to
  // zero and causes component_count to return 2.
  // Calling set_to_default_member on a union that does not have an explicit
  // default case raises TypeMismatch.
  public void set_to_default_member()
      throws org.omg.DynamicAny.DynAnyPackage.TypeMismatch {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    int defaultIndex = defaultIndex();
    if (defaultIndex == -1) {
      throw new TypeMismatch();
    }
    try {
      clearData();
      index = 1;
      currentMemberIndex = defaultIndex;
      currentMember = DynAnyUtil.createMostDerivedDynAny(memberType(defaultIndex), orb);
      components = new DynAny[]{discriminator, currentMember};
      Any discriminatorAny = orb.create_any();
      discriminatorAny.insert_octet((byte) 0);
      discriminator = DynAnyUtil.createMostDerivedDynAny(discriminatorAny, orb, false);
      representations = REPRESENTATION_COMPONENTS;
    } catch (InconsistentTypeCode ictc) {
    }
  }

  // Sets the discriminator to a value that does not correspond
  // to any of the unions case labels.
  // It sets the current position to zero and causes component_count to return 1.
  // Calling set_to_no_active_member on a union that has an explicit default case
  // or on a union that uses the entire range of discriminator values
  // for explicit case labels raises TypeMismatch.
  public void set_to_no_active_member()
      throws org.omg.DynamicAny.DynAnyPackage.TypeMismatch {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    // _REVISIT_ How does one check for "entire range of discriminator values"?
    if (defaultIndex() != -1) {
      throw new TypeMismatch();
    }
    checkInitComponents();
    Any discriminatorAny = getAny(discriminator);
    // erase the discriminators value so that it does not correspond
    // to any of the unions case labels
    discriminatorAny.type(discriminatorAny.type());
    index = 0;
    currentMemberIndex = NO_INDEX;
    // Necessary to guarantee OBJECT_NOT_EXIST in member()
    currentMember.destroy();
    currentMember = null;
    components[0] = discriminator;
    representations = REPRESENTATION_COMPONENTS;
  }

  // Returns true if the union has no active member
  // (that is, the unions value consists solely of its discriminator because the
  // discriminator has a value that is not listed as an explicit case label).
  // Calling this operation on a union that has a default case returns false.
  // Calling this operation on a union that uses the entire range of discriminator
  // values for explicit case labels returns false.
  public boolean has_no_active_member() {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    // _REVISIT_ How does one check for "entire range of discriminator values"?
    if (defaultIndex() != -1) {
      return false;
    }
    checkInitComponents();
    return (checkInitComponents() ? (currentMemberIndex == NO_INDEX) : false);
  }

  public org.omg.CORBA.TCKind discriminator_kind() {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    return discriminatorType().kind();
  }

  // Returns the currently active member.
  // If the union has no active member, the operation raises InvalidValue.
  // Note that the returned reference remains valid only for as long
  // as the currently active member does not change.
  // Using the returned reference beyond the life time
  // of the currently active member raises OBJECT_NOT_EXIST.
  public org.omg.DynamicAny.DynAny member()
      throws org.omg.DynamicAny.DynAnyPackage.InvalidValue {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    if (!checkInitComponents() || currentMemberIndex == NO_INDEX) {
      throw new InvalidValue();
    }
    return currentMember;
  }

  // Returns the name of the currently active member.
  // If the unions TypeCode does not contain a member name for the currently active member,
  // the operation returns an empty string.
  // Calling member_name on a union without an active member raises InvalidValue.
  public String member_name()
      throws org.omg.DynamicAny.DynAnyPackage.InvalidValue {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    if (!checkInitComponents() || currentMemberIndex == NO_INDEX) {
      throw new InvalidValue();
    }
    String memberName = memberName(currentMemberIndex);
    return (memberName == null ? "" : memberName);
  }

  // Returns the TCKind value of the TypeCode of the currently active member.
  // If the union has no active member, the operation raises InvalidValue.
  public org.omg.CORBA.TCKind member_kind()
      throws org.omg.DynamicAny.DynAnyPackage.InvalidValue {
    if (status == STATUS_DESTROYED) {
      throw wrapper.dynAnyDestroyed();
    }
    if (!checkInitComponents() || currentMemberIndex == NO_INDEX) {
      throw new InvalidValue();
    }
    return memberType(currentMemberIndex).kind();
  }
}
